package com.hp.snap.gr.action.impl;


import com.hp.snap.gr.exception.ActionException;
import com.hp.snap.gr.exception.RESTfulException;
import com.hp.snap.gr.model.jaxb.ActionType;
import com.hp.snap.gr.model.jaxb.RESTfulInvokeActionType;
import com.hp.snap.gr.model.jaxb.RESTfulParameterType;
import com.hp.snap.gr.utils.Constants;
import com.hp.snap.gr.utils.RESTfulUtils;
import com.hp.snap.gr.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RESTfulInvokeActionHandler extends AbstractActionHandler {
    public static final String RESPONSE_CODE = "__restful_response_code";
    public static final String RESPONSE_TEXT = "__restful_response_text";

    private static Logger logger = LoggerFactory.getLogger(RESTfulInvokeActionHandler.class);

    private static Pattern URL_PATTERN = Pattern.compile("http://([^/:]+):?(\\d+)?(.*)");

    @Override
    public void execute(ActionType action,
                        Map<String, Object> actionPlanInputs,
                        Map<String, Object> actionPlanOutputs) throws ActionException {
        if (action instanceof RESTfulInvokeActionType) {
            RESTfulInvokeActionType actionType = (RESTfulInvokeActionType) action;

            String host = null;
            int port = 80;
            String uri = null;
            Matcher m = URL_PATTERN.matcher(actionType.getURI());
            if (m.find()) {
                host = m.group(1);
                if (m.groupCount() == 3) {
                    port = Integer.parseInt(m.group(2));
                    uri = m.group(3);
                } else {
                    uri = m.group(2);
                }
            } else {
                throw new ActionException("Illegal URL parameter");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("going to invoke RESTful http://{}:{}{}", new Object[]{host, port, uri});
            }
            try {
                String result = RESTfulUtils.getRESTfulInvokeResult(host, port, uri, actionType.getMethod(), actionType.getMethod().equals(Constants.HTTP_METHOD_POST) ? constructMap(actionType.getParameter()) : constructUri(actionType.getParameter()));

                actionPlanOutputs.put(RESPONSE_CODE, 200);
                actionPlanOutputs.put(RESPONSE_TEXT, result);
            } catch (RESTfulException e) {
                if (logger.isDebugEnabled()) {
                    logger.debug("RESTful service invoked failed, " + e.getMessage(), e);
                }
                actionPlanOutputs.put(RESPONSE_CODE, e.getStatusCode());
                actionPlanOutputs.put(RESPONSE_TEXT, null);
            } catch (UnsupportedEncodingException e) {
                if (logger.isDebugEnabled()) {
                    logger.debug("construct RESTful URL failed, {}", e.getMessage(), e);
                }
                actionPlanOutputs.put(RESPONSE_CODE, 500);
                actionPlanOutputs.put(RESPONSE_TEXT, e.getMessage());
            }
        }
    }

    private String constructUri(List<RESTfulParameterType> parameter) throws UnsupportedEncodingException {
        if (parameter == null || parameter.isEmpty()) return "";
        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < parameter.size(); i++) {
            RESTfulParameterType p = parameter.get(i);
            sb.append(p.getName()).append("=").append(URLEncoder.encode(p.getVal(), "UTF-8"));
            if (i != parameter.size() - 1) sb.append("&");
        }
        return sb.toString();
    }

    private String constructMap(List<RESTfulParameterType> params) {
        Map<String, String> paramMap = new HashMap<String, String>();
        for (RESTfulParameterType param : params) {
            paramMap.put(param.getName(), param.getVal());
        }

        return Utils.convertMap2Json(paramMap);
    }
}
